
#ifndef __Math_H__
#define __Math_H__

#include "float.h"
#include "MathPrerequisites.h"
namespace Apo
{


 

    class  LsMath 
    {
   public:
       /** The angular units used by the API. This functionality is now deprecated in favor
	       of discreet angular unit types ( see Degree and Radian above ). The only place
		   this functionality is actually still used is when parsing files. Search for
		   usage of the Angle class for those instances
       */
       enum AngleUnit
       {
           AU_DEGREE,
           AU_RADIAN
       };

    protected:
       // angle units used by the api
       static AngleUnit msAngleUnit;

        /// Size of the trig tables as determined by constructor.
        static int mTrigTableSize;

        /// Radian -> index factor value ( mTrigTableSize / 2 * PI )
        static float mTrigTableFactor;
        static float* mSinTable;
        static float* mTanTable;

        /** Private function to build trig tables.
        */
        void buildTrigTables();

		static float SinTable (float fValue);
		static float TanTable (float fValue);
    public:
        /** Default constructor.
            @param
                trigTableSize Optional parameter to set the size of the
                tables used to implement Sin, Cos, Tan
        */
        LsMath(unsigned int trigTableSize = 4096);

        /** Default destructor.
        */
        ~LsMath();

		static inline int IAbs (int iValue) { return ( iValue >= 0 ? iValue : -iValue ); }
		static inline int ICeil (float fValue) { return int(ceil(fValue)); }
		static inline int IFloor (float fValue) { return int(floor(fValue)); }
        static int ISign (int iValue);

		static inline float Abs (float fValue) { return float(fabs(fValue)); }
 
		static inline int Abs (int fValue) 
 		{
 			int y = fValue >> 31; 
 			return ( ( fValue ^ y ) - y );
 		}

 

		static inline float Ceil (float fValue) { return float(ceil(fValue)); }
		static inline bool isNaN(float f)
		{
			// std::isnan() is C99, not supported by all compilers
			// However NaN always fails this next test, no other number does.
			return f != f;
		}

 
        /** Cosine function.
            @param
                fValue Angle in radians
            @param
                useTables If true, uses lookup tables rather than
                calculation - faster but less accurate.
        */
        static inline float Cos (float fValue, bool useTables = false) {
			return (!useTables) ? float(cos(fValue)) : SinTable(fValue + HALF_PI);
		}

		static inline float Exp (float fValue) { return float(exp(fValue)); }

		static inline float Floor (float fValue) { return float(floor(fValue)); }

		static inline float Log (float fValue) { return float(log(fValue)); }

		/// Stored value of log(2) for frequent use
		static const float LOG2;

		static inline float Log2 (float fValue) { return float(log(fValue)/LOG2); }

		static inline float LogN (float base, float fValue) { return float(log(fValue)/log(base)); }

		static inline float Pow (float fBase, float fExponent) { return float(pow(fBase,fExponent)); }

        static float Sign (float fValue);
 
	 
 
        /** Sine function.
            @param
                fValue Angle in radians
            @param
                useTables If true, uses lookup tables rather than
                calculation - faster but less accurate.
        */
        static inline float Sin (float fValue, bool useTables = false) {
			return (!useTables) ? float(sin(fValue)) : SinTable(fValue);
		}

		static inline float Sqr (float fValue) { return fValue*fValue; }

		static inline float Sqrt (float fValue) { return float(sqrt(fValue)); }

       

        /** Inverse square root i.e. 1 / Sqrt(x), good for vector
            normalisation.
        */
		static float InvSqrt(float fValue);

        static float UnitRandom ();  // in [0,1]

        static float RangeRandom (float fLow, float fHigh);  // in [fLow,fHigh]

        static float SymmetricRandom ();  // in [-1,1]

  
        /** Tangent function.
            @param
                fValue Angle in radians
            @param
                useTables If true, uses lookup tables rather than
                calculation - faster but less accurate.
        */
		static inline float Tan (float fValue, bool useTables = false) {
			return (!useTables) ? float(tan(fValue)) : TanTable(fValue);
		}

		static inline float DegreesToRadians(float degrees) { return degrees * fDeg2Rad; }
        static inline float RadiansToDegrees(float radians) { return radians * fRad2Deg; }

       /** These functions used to set the assumed angle units (radians or degrees) 
            expected when using the Angle type.
       @par
            You can set this directly after creating a new Root, and also before/after resource creation,
            depending on whether you want the change to affect resource files.
       */
       static void setAngleUnit(AngleUnit unit);
       /** Get the unit being used for angles. */
       static AngleUnit getAngleUnit(void);

       /** Convert from the current AngleUnit to radians. */
       static float AngleUnitsToRadians(float units);
       /** Convert from radians to the current AngleUnit . */
       static float RadiansToAngleUnits(float radians);
       /** Convert from the current AngleUnit to degrees. */
       static float AngleUnitsToDegrees(float units);
       /** Convert from degrees to the current AngleUnit. */
       static float DegreesToAngleUnits(float degrees);

 
        static bool pointInTri2D(const Vector2& p, const Vector2& a, 
			const Vector2& b, const Vector2& c);

       /** Checks whether a given 3D point is inside a triangle.
       @remarks
            The vertices of the triangle must be given in either
            trigonometrical (anticlockwise) or inverse trigonometrical
            (clockwise) order, and the point must be guaranteed to be in the
			same plane as the triangle
        @param
            p The point.
        @param
            a The triangle's first vertex.
        @param
            b The triangle's second vertex.
        @param
            c The triangle's third vertex.
		@param 
			normal The triangle plane's normal (passed in rather than calculated
				on demand since the caller may already have it)
        @returns
            If the point resides in the triangle, <b>true</b> is
            returned.
        @par
            If the point is outside the triangle, <b>false</b> is
            returned.
        */
        static bool pointInTri3D(const Vector3& p, const Vector3& a, 
			const Vector3& b, const Vector3& c, const Vector3& normal);
  
 


       
 
        /** Compare 2 reals, using tolerance for inaccuracies.
        */
        static bool RealEqual(float a, float b,
            float tolerance = FLT_EPSILON/*std::numeric_limits<float>::epsilon()*/);

        /** Calculates the tangent space vector for a given set of positions / texture coords. */
        static Vector3 calculateTangentSpaceVector(
            const Vector3& position1, const Vector3& position2, const Vector3& position3,
            float u1, float v1, float u2, float v2, float u3, float v3);

     
        /** Calculate a face normal without normalize, no w-information. */
        static Vector3 calculateBasicFaceNormalWithoutNormalize(const Vector3& v1, const Vector3& v2, const Vector3& v3);

		/** Generates a value based on the Gaussian (normal) distribution function
			with the given offset and scale parameters.
		*/
		static float gaussianDistribution(float x, float offset = 0.0f, float scale = 1.0f);

		/** Clamp a value within an inclusive range. */
		template <typename T>
		static T Clamp(T val, T minval, T maxval)
		{
			assert (minval < maxval && "Invalid clamp range");
			return max(min(val, maxval), minval);
		}

	  

        static const float POS_INFINITY;
        static const float NEG_INFINITY;
        static const float PI;
        static const float TWO_PI;
        static const float HALF_PI;
		static const float fDeg2Rad;
		static const float fRad2Deg;

    };

 
 
}
#endif
